﻿using BootstrapBlazorApp.Shared.Data.SYS;
using BootstrapBlazorApp.Shared.Services;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Concurrent;

namespace BootstrapBlazorApp.Shared.Shared;

/// <summary>
/// 账票状态上下文
/// </summary>
public class TokenContext : ITokenContext
{
    ////常用值，单独存放，提高效率
    //private bool _userUnlimit;
    //private IEnumerable? _globalApps;
    //private IEnumerable<string>? _userPermissions;

    /// <summary>
    /// 会话字典
    /// </summary>
    private readonly ConcurrentDictionary<string, object?> _sessions = new();

    /// <summary>
    /// 
    /// </summary>
    /// <param name="interceptor"></param>
    public TokenContext(ITokenInterceptor interceptor)
    {
        Interceptor = Application.Instance.GetService<ITokenInterceptor>();
        //Interceptor = interceptor;
    }

    /// <summary>
    /// 当前Token的拦截器
    /// </summary>
    public ITokenInterceptor Interceptor { get; private set; } 

    /// <summary>
    /// 授权账票
    /// </summary>
    public string? AccessToken { get; private set; }

    /// <summary>
    /// 刷新账票
    /// </summary>
    public string? RefreshToken { get; private set; }

    /// <summary>
    /// 指示授权账票过期时间
    /// </summary>
    public long AccessExpir { get; private set; }

    /// <summary>
    /// 指示授权账票过期时间
    /// </summary>
    public long RefreshExpir { get; private set; }

    /// <summary>
    /// 指示Token临近过期或已经过期（提前60秒）
    /// </summary>
    public bool IsExpiringSoon
    {
        get
        {
            var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            return AccessExpir - now < 60 || RefreshExpir - now < 60;
        }
    }

    public bool IsAuthenticated { get; private set; }

    /// <summary>
    /// 获得/设置 是否需要刷新客户端本地Token
    /// </summary>
    public bool NeedRefreshClientToken { get; private set; }

    public CurrentUser CurrentUser =>  GetSession<CurrentUser>(ClientAppKey.CurrentUser);

    /// <summary>
    /// 重载应用
    /// </summary>
    public event Action? ReloadApplication;

    /// <summary>
    /// 刷新客户端会话Token事件
    /// </summary>
    public event EventHandler<bool>? RefreshClientToken;

    /// <summary>
    /// 更新客户端LocalStorage的Token事件
    /// </summary>
    public event EventHandler<(string?, string?)>? UpdateClientToken;

    /// <summary>
    /// 读取Token信息
    /// </summary>
    /// <param name="state"></param>
    public void LoadToken(AuthenticationState? state)
    {
        if (state?.User.Identity?.IsAuthenticated == true)
        {
            IsAuthenticated = true;
            AccessToken = state.User.FindFirst(x => x.Type == "AccessToken")?.Value;
            AccessExpir = TokenHelper.GerTokenExpir(state.User.Claims, "AccessExpir");
            RefreshToken = state.User.FindFirst(x => x.Type == "RefreshToken")?.Value;
            RefreshExpir = TokenHelper.GerTokenExpir(state.User.Claims, "RefreshExpir");
        }
    }

    /// <summary>
    /// 更新Token信息
    /// </summary>
    /// <param name="isAuthenticated"></param>
    /// <param name="accessToken"></param>
    /// <param name="refreshToken"></param>
    public void UpdateToken(bool isAuthenticated, string? accessToken, string? refreshToken)
    {
        if (AccessToken == accessToken && RefreshToken == refreshToken)
        {
            return;
        }

        IsAuthenticated = isAuthenticated;
        AccessToken = accessToken;
        AccessExpir = TokenHelper.GerTokenExpir(AccessToken, "exp");
        RefreshToken = refreshToken;
        RefreshExpir = TokenHelper.GerTokenExpir(RefreshToken, "exp");

        //标记状态，写入到客户端LocalStorage
        NeedRefreshClientToken = true;
        RefreshClientToken?.Invoke(null, IsAuthenticated);
    }

    /// <summary>
    /// 客户端LocalStorage的Token更新处理回调方法
    /// </summary>
    public void UpdateClientTokenCallback()
    {
        //_semaphore.WaitOne();
        if (NeedRefreshClientToken)
        {
            NeedRefreshClientToken = false;
            UpdateClientToken?.Invoke(null, (AccessToken, RefreshToken));
        }
        //_semaphore.Release();
    }

    /// <summary>
    /// 重载应用
    /// </summary>
    public void ReloadApp() => ReloadApplication?.Invoke();

    /// <summary>
    /// 检查当前请求 许可 是否已授权
    /// </summary>
    /// <param name="permission">许可</param>
    /// <returns></returns>
    public bool Permission(string permission)
    {
        if (string.IsNullOrWhiteSpace(permission))
        {
            return false;
        }

        //if (_userUnlimit)
        //{
        //    return true;
        //}

        //if (_userPermissions is null)
        //{
        //    return false;
        //}

        //return _userPermissions.Any(x => x.Equals(permission, StringComparison.OrdinalIgnoreCase));

        return true;
    }

    /// <summary>
    /// 获取会话值
    /// </summary>
    /// <param name="key"></param>
    public TValue? GetSession<TValue>(string key)
    {
        if (string.IsNullOrWhiteSpace(key))
        {
            return default;
        }

        if (!_sessions.ContainsKey(key))
        {
            return default;
        }

        if (_sessions.TryGetValue(key, out object? obj))
        {
            if (obj is not null && obj is TValue value)
            {
                return value;
            }
        }

        return default;
    }

    /// <summary>
    /// 是否存在会话键
    /// </summary>
    /// <param name="key"></param>
    public bool HasSession(string key)
    {
        if (string.IsNullOrWhiteSpace(key))
        {
            return false;
        }

        return _sessions.ContainsKey(key);
    }

    /// <summary>
    /// 更新会话值
    /// </summary>
    /// <param name="key">键</param>
    /// <param name="value">值</param>
    public bool SetSession<TValue>(string key, TValue? value)
    {
        if (string.IsNullOrWhiteSpace(key))
        {
            return false;
        }

        _sessions.AddOrUpdate(key, value, UpdateValueFactory);
        return true;
    }

    /// <summary>
    /// 移除会话值
    /// </summary>
    /// <param name="key"></param>
    public bool RemoveSession(string key)
    {
        if (_sessions.ContainsKey(key))
        {
            return _sessions.Remove(key, out var obj);
        }

        return true;
    }

    /// <summary>
    /// 清空会话值
    /// </summary>
    public void ClearSession()
    {
        _sessions.Clear();
        UpdateToken(false, null, null);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    private static TValue UpdateValueFactory<TKey, TValue>(TKey key, TValue value) => value;
}

